package com.chixing.utils;

import com.chixing.entity.DTO.MessageDTO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ServerEndpoint(value = "/ws/{userId}")
public class WebSocketProcess {
    //会话对象
    private Session session;
    //将每个对象都放到ConcurrentHashMap
    private static ConcurrentHashMap<Long , WebSocketProcess> concurrentHashMap=new ConcurrentHashMap<>();

    @Autowired
    private ObjectMapper objectMapper; // 注入全局 ObjectMapper
    @Autowired
    private static RedisTemplate redisTemplate;
    @Autowired
    public void setRedisTemplate(RedisTemplate<String,Object> redisTemplate){
        WebSocketProcess.redisTemplate = redisTemplate;
    }

    //redis中的在线用户key
    private static final String ONLINE_USER = "online_user";
    //redis中离线用户的key
    private static final String OFFLINE_MSG = "offline_user";

    /**
     * 客户端连接时触发
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") Long id){
        //没建立一个连接，就把当前客户的id设为key，this为value保存到map中
        this.session = session;
        concurrentHashMap.put(id,this);
        System.out.println("用户连接成功:"+id);
        redisTemplate.opsForSet().add(ONLINE_USER,id);
        //补发消息，检查redis中是否有该用户的离线信息
        if (redisTemplate.hasKey(OFFLINE_MSG+id)){
            System.out.println("redis中有用户:"+id+"的离线消息");
            //获得离线消息
            List<String> allMsg = redisTemplate.opsForList().range(OFFLINE_MSG+id,0,-1);
            if (allMsg!=null){
                for (String msg:allMsg) {
                    System.out.println("Redis中该用户:"+id+"保存在redis中的消息内容是:"+msg);
                    try {
                        session.getBasicRemote().sendText(msg);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
                redisTemplate.delete(OFFLINE_MSG+id);
            }
        }
    }

    /**
     * 客户端断开时触发
     */
    @OnClose
    public void onClose(Session session,@PathParam("userId") Long id){
        //客户端连接关闭时，移除mapper中的键值对
        concurrentHashMap.remove(id);
        if (redisTemplate!=null){
            redisTemplate.opsForSet().remove(ONLINE_USER,id);
            System.out.println("用户断开连接:"+id);
        }
    }

    /**
     * 发送消息到指定客户端
     */
    public boolean sendMessage(Long id , MessageDTO messageDTO) throws IOException {
        //把对象转化为字符串
//        ObjectMapper objectMapper = new ObjectMapper();
        String message = objectMapper.writeValueAsString(messageDTO);
        //根据id从map中获取存储的websocket对象
        WebSocketProcess webSocketProcess = concurrentHashMap.get(id);
        //判断对象是否存在
        if (!ObjectUtils.isEmpty(webSocketProcess)){
            //判断连接是否打开
            if (webSocketProcess.session.isOpen()){
                System.out.println(">>>>>>>>用户"+id +"在线的，服务器正在推送消息");
                webSocketProcess.session.getBasicRemote().sendText(message);
                return true;
            }else {//用户离线：保存到redis中
                System.out.println(">>>>>>>>服务器发送消息给用户"+id +"但是用户不在线，保存到离线消息的redis中");
                String key = OFFLINE_MSG + id;
                Boolean hasKey = redisTemplate.hasKey(key);
                redisTemplate.opsForList().leftPush(key, message);
                //只有在第一次创建这个key的时候才需要加过期时间，不然可能会导致永不过期
                if (Boolean.FALSE.equals(hasKey)) {
                    redisTemplate.expire(key, Duration.ofMinutes(30));
                }
                return false;
            }
        }else {
            redisTemplate.opsForList().leftPushAll(OFFLINE_MSG+id,message);
            return false;
        }
    }


}
